#include "config.h"
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <stdexcept>
#include <algorithm>

Color Color::fromHex(const std::string &hex)
{
    Color c = {0xFF, 0xFF, 0xFF, 0xFF};
    if (hex.empty() || hex[0] != '#')
        return c;

    std::string val = hex.substr(1);
    try
    {
        if (val.size() == 6 || val.size() == 8)
        {
            c.r = std::stoul(val.substr(0, 2), nullptr, 16);
            c.g = std::stoul(val.substr(2, 2), nullptr, 16);
            c.b = std::stoul(val.substr(4, 2), nullptr, 16);
            if (val.size() == 8)
                c.a = std::stoul(val.substr(6, 2), nullptr, 16);
        }
        else if (val.size() == 3 || val.size() == 4)
        {
            c.r = std::stoul(val.substr(0, 1), nullptr, 16) * 0x11;
            c.g = std::stoul(val.substr(1, 1), nullptr, 16) * 0x11;
            c.b = std::stoul(val.substr(2, 1), nullptr, 16) * 0x11;
            if (val.size() == 4)
                c.a = std::stoul(val.substr(3, 1), nullptr, 16) * 0x11;
        }
    }
    catch (const std::invalid_argument &)
    { /* Fallback to default */
    }
    return c;
}

bool Config::loadFromFile(const std::string &path)
{
    std::string filePath = path;
    if (filePath.empty())
    {
        const char *home = getenv("HOME");
        if (home)
            filePath = std::string(home) + "/.config/dmenu/config";
        else
            return false;
    }

    std::ifstream file(filePath);
    if (!file.is_open())
        return false;

    std::string line;
    while (std::getline(file, line))
    {
        line.erase(line.begin(), std::find_if(line.begin(), line.end(), [](unsigned char ch)
                                              { return !isspace(ch); }));
        if (line.empty() || line[0] == '#')
            continue;

        size_t eqPos = line.find('=');
        if (eqPos == std::string::npos)
            continue;

        std::string key = line.substr(0, eqPos);
        std::string val = line.substr(eqPos + 1);

        key.erase(std::find_if(key.rbegin(), key.rend(), [](unsigned char ch)
                               { return !isspace(ch); })
                      .base(),
                  key.end());
        val.erase(val.begin(), std::find_if(val.begin(), val.end(), [](unsigned char ch)
                                            { return !isspace(ch); }));
        val.erase(std::find_if(val.rbegin(), val.rend(), [](unsigned char ch)
                               { return !isspace(ch); })
                      .base(),
                  val.end());

        if (key == "topbar")
            topbar = (val == "true");
        else if (key == "lines")
            lines = std::stoi(val);
        else if (key == "prompt")
            prompt = val;
        else if (key == "centered")
            centered = (val == "true");
        else if (key == "min_width")
            min_width = std::stoi(val);
        else if (key == "menu_height_ratio")
            menu_height_ratio = std::stof(val);
        else if (key == "word_delimiters")
            word_delimiters = val;
        else if (key == "case_insensitive")
            case_insensitive = (val == "true");
        else if (key == "fast_mode")
            fast_mode = (val == "true");
        else if (key == "monitor")
            monitor = std::stoi(val);
        else if (key == "embed_window")
            embed_window = std::stoull(val, nullptr, 16);
        else if (key.substr(0, 5) == "font_")
        {
            size_t idx = std::stoul(key.substr(5));
            if (idx >= fonts.size())
                fonts.resize(idx + 1);
            fonts[idx] = val;
        }
        else if (key == "color_norm_fg")
            colors[(size_t)Scheme::NORM][0] = Color::fromHex(val);
        else if (key == "color_norm_bg")
            colors[(size_t)Scheme::NORM][1] = Color::fromHex(val);
        else if (key == "color_sel_fg")
            colors[(size_t)Scheme::SEL][0] = Color::fromHex(val);
        else if (key == "color_sel_bg")
            colors[(size_t)Scheme::SEL][1] = Color::fromHex(val);
        else if (key == "color_out_fg")
            colors[(size_t)Scheme::OUT][0] = Color::fromHex(val);
        else if (key == "color_out_bg")
            colors[(size_t)Scheme::OUT][1] = Color::fromHex(val);
    }
    return true;
}

bool Config::loadFromArgs(int argc, char *argv[])
{
    for (int i = 1; i < argc; ++i)
    {
        std::string arg = argv[i];
        if (arg == "-v")
        {
            printf("dmenu-5.2 (custom build)\n");
            exit(0);
        }
        else if (arg == "-b")
            topbar = false;
        else if (arg == "-f")
            fast_mode = true;
        else if (arg == "-i")
            case_insensitive = true;
        else if (arg == "-c")
            centered = true;
        else if (i + 1 >= argc)
            return false;
        else if (arg == "-l")
            lines = std::stoi(argv[++i]);
        else if (arg == "-m")
            monitor = std::stoi(argv[++i]);
        else if (arg == "-p")
            prompt = argv[++i];
        else if (arg == "-fn")
        {
            fonts[0] = argv[++i];
            if (fonts.size() > 1)
                fonts.resize(1);
        }
        else if (arg == "-nb")
            colors[(size_t)Scheme::NORM][1] = Color::fromHex(argv[++i]);
        else if (arg == "-nf")
            colors[(size_t)Scheme::NORM][0] = Color::fromHex(argv[++i]);
        else if (arg == "-sb")
            colors[(size_t)Scheme::SEL][1] = Color::fromHex(argv[++i]);
        else if (arg == "-sf")
            colors[(size_t)Scheme::SEL][0] = Color::fromHex(argv[++i]);
        else if (arg == "-w")
            embed_window = std::stoull(argv[++i], nullptr, 16);
        else
            return false;
    }
    return true;
}

void usage()
{
    fprintf(stderr,
            "Usage: dmenu [OPTIONS]\n"
            "  -b                Bottom alignment\n"
            "  -f                Fast mode (skip initial focus wait)\n"
            "  -i                Case-insensitive matching\n"
            "  -c                Centered menu\n"
            "  -v                Print version and exit\n"
            "  -l <lines>        Number of visible lines (default: 5)\n"
            "  -p <prompt>       Prompt text (default: \"❯ \")\n"
            "  -fn <font>        Font (e.g., \"Noto Sans:size=12\")\n"
            "  -nb <color>       Normal background color (hex)\n"
            "  -nf <color>       Normal foreground color (hex)\n"
            "  -sb <color>       Selected background color (hex)\n"
            "  -sf <color>       Selected foreground color (hex)\n"
            "  -m <monitor>      Monitor index (0-based, default: focused)\n"
            "  -w <windowid>     Embed in window (hex ID)\n");
    exit(EXIT_FAILURE);
}
